該系列是為了讓看過Vue官方文件或學過Vue但是卻不知道怎麼下手去重構現在有的網站而去規畫的系列文章,在這邊整理了許多我自己使用Vue重構很多網站的經驗分享給讀者們。
我們在Vue要使用跨多個 component 做資料傳遞溝通的時候除了 Vuex 以外,在 Vue3 的時候我們有多了其他選擇,那就是 Provide / inject 。
我們可以看到官網上面的這張圖就完整的表達 Provide / inject 的使用概念,你可以在父層使用provide來提供資料,然後再子 component 透過 inject 來使用這些資料,無論你的 component 結構有多深,你的父層都可以為你所有底下的所有的 component 去直接 inject。
我們來看看如何實作,先來看看資料夾結構,其他資料夾等我就先不放上來了
|-- src
|-- App.vue // 最上層的component
|-- main.js // 進入點
|-- components
| |-- Content.vue // 一般的UI component
|-- store
| |-- index.js // 不是Vuex,是要使用 Composition API 存放資料的地方
|-- views
|-- Home.vue // 首頁的頁面component
首先我們先來定義 store/index.js
import { reactive, readonly } from "vue";
const state = reactive({
count: 0,
});
export default {
state: readonly(state),
};
在這邊你會看到我透過 vue3 的reactive
去定義我的資料,透過 vue 的方法可以確保我的資料被更動的時候可以被更新,當然你可以用 ref
,就依照你的需求來決定,最後我在 export 的時候用了 readonly
函數包起來,就是要避免外面 inject
的時候不小心改到 state 的資料,至於要怎麼改資料我們等等來說,我們先來看要怎麼在component 去使用。
App.vue
<script>
import { provide } from "vue";
import store from "@/store";
import Home from "@/views/Home.vue";
export default {
components: {
Home,
},
setup() {
provide("mapStore", store);
},
};
</script>
我在 setup 裡面將我們 provide
命名為 mapStore
( 借鏡一下 Vuex 的命名,這樣從 Vuex 轉換過來也會比較親切 ) ,然後把我剛剛定義好的資料給載入進來,就這麼簡單。
Content.vue
<script>
import { inject, toRefs } from "vue";
export default {
setup() {
const mapStore = inject("mapStore");
const { state } = mapStore;
return {
...toRefs(state),
};
},
};
</script>
<template>
<div class="content">
<h1>count: {{ count }}</h1>
<button>add</button>
<button>remove</button>
</div>
</template>
這邊透過 inject 的方式把我的 mapStore 給注入進來,接下來我就可以直接取得我定義好的state 資料,然後丟到我的template上,不過這邊要注意我使用了toRefs 去做包我的 state,為什麼不直接把 state 丟出去呢?
原因是因為透過 reactive 包裝的資料,是透過解構的方式取出的話,會失去它的連動特性,所以這個時候在需要透過 toRefs 的包裝,來讓它維持資料的連動,最後把我定義好的 count 這個資料放到畫面上
toRefs 詳情可以參考文件 https://v3.cn.vuejs.org/api/refs-api.html#torefs
現在的畫面長這樣
假設今天我直接增加一個 function去修改我拿到的count的話
<script>
import { inject, toRefs } from "vue";
export default {
setup() {
const mapStore = inject("mapStore");
const { state } = mapStore;
const headleClick = () => {
state.count++;
};
return {
...toRefs(state),
headleClick,
};
},
};
</script>
<template>
<div class="content">
<h1>count: {{ count }}</h1>
<button @click="headleClick">add</button>
<button>remove</button>
</div>
</template>
你 click 它 log 會跳出說這個資料它是唯獨的,不可以修改
這是因為我們在 store/index.js 裡面透過readonly回傳的,所以我們不能這樣修改,readonly的好處就是要避免開發者直接修改資料,所以我們應該另外去定義可以修改的資料的 function,讓資料歸資料,修改交給其他方法去做。
我們在 store/index.is 上面新增了add 跟 remove 的兩個 functino
import { reactive, readonly } from "vue";
const state = reactive({
count: 0,
});
const addCount = () => {
state.count++;
};
const removeCount = () => {
state.count--;
};
export default {
state: readonly(state),
addCount,
removeCount,
};
然後把 function 取出來就可以掛在我們的 button,就可以成功的修改資料
<script>
import { inject, toRefs } from "vue";
export default {
setup() {
const mapStore = inject("mapStore");
const { state, addCount, removeCount } = mapStore;
return {
...toRefs(state),
addCount,
removeCount,
};
},
};
</script>
<template>
<div class="content">
<h1>count: {{ count }}</h1>
<button @click="addCount">add</button>
<button @click="removeCount">remove</button>
</div>
</template>
透過這樣的方式我們就可以不需要透過 Vuex 的方式來共用資料,你可能會問說如果要像 actions 一樣處理非同步的 API 要怎麼辦 ?
一樣我們可以把非同步給放到store/index.js裡面,這邊我就寫一個大概的示範
const fetchUserData = async () => {
try {
const res = await axios.get("https://www.api.com/api/user");
const { name, age, address } = res.data;
state.name = name;
state.age = age;
state.address = address;
} catch (error) {
state.errorMessage = "This user is not found!";
}
};
這邊其實我們就把 store/index.js 給當成 Vuex 使用那樣來使用,有 state、actions/mutations,如果你想要有getter 的功能的話也可以使用 computed 來替代達成。
import { computed, reactive, readonly } from "vue";
const userInfo = computed(()=> {
return `Info: ${state.name}, ${state.age}, ${state.address}`
})
export default {
state: readonly(state),
addCount,
removeCount,
userInfo
};
這也是很多人在說為什麼使用 Vue3 的時候可能不需要在使用 Vuex 的原因,因為我們就可以利用Provide / inject來達成使用Vuex管理資料的作法。
我自己的使用下來歸納幾個想法
最後殺雞焉用牛刀,我們不管用哪個都沒有對錯,就只有適不適合而已,今天在這邊介紹了如何使用 Provide / inject,下一篇我們來談談 Vue3 使用 Vuex 的一些進階的用法。
Ps. 購買的時候請登入或註冊該平台的會員,然後再使用下面連結進入網站點擊「立即購課」,這樣才可以讓我獲得更多的課程分潤,還可以幫助我完成更多豐富的內容給各位。
我有開設了一堂專門針對Vue3從零開始教學的課程,如果你覺得不錯的話,可以購買我課程來學習
https://hiskio.com/bundles/9WwPNYRpz?s=tc
那如果對於JS基礎不熟的朋友,我也有開設JS的入門課程,可以參考這個課程
https://hiskio.com/bundles/b9Rovqy7z?s=tc
Mike 的 Youtube 頻道
Mike的medium
MIke 的官方 line 帳號,好友搜尋 @mike_cheng
您好,咨询一下,提取了store/index.js,那应该可以在使用到数据的地方直接引入该文件即可。
为什么要选择在上层(根)组件上使用provide
,在应用组件使用inject
引入呢?